#include <ClanLib/core.h>
#include <ClanLib/application.h>
#include <ClanLib/display.h>
#ifdef USE_SDL
	#include <ClanLib/sdl.h>
#else
	#include <ClanLib/gl.h>
#endif
#include "particles.h"
#include "viewport.h"
#include "random.h"
#include <list>
#include <math.h>

//Emitter
Emitter::Emitter()
{
	particle = NULL;
	particleCount = 0;
}
Emitter::~Emitter()
{
	if(particle != NULL)
	{
		while(particle->next != NULL)
		{
			particle = particle->next;
			free(particle->prev);
		}
		free(particle);
	}
}

//ParticleManager

ParticleManager::ParticleManager()
{
	int i;
	this->particlePool = new Particle;

	this->particlePool->next = NULL;
	for (i = 0; i < 1000; i++)
	{
		this->particlePool->prev = new Particle;
		this->particlePool->prev->next = this->particlePool;
		this->particlePool = this->particlePool->prev;
	}
	this->particlePool->prev = NULL;
}

ParticleManager::~ParticleManager()
{
	if(particlePool != NULL)
	{
		while(particlePool->next != NULL)
		{
			particlePool = particlePool->next;
			free(particlePool->prev);
		}
		free(particlePool);
	}	
}

void ParticleManager::addEmitter(Emitter &e)
{
	emitter.push_front( e );
}

void ParticleManager::clearEmitter()
{
	emitter.clear();
}

void ParticleManager::update(Viewport &viewport)
{
	std::list<Emitter>::iterator e;
	Particle *p;
	int emitNumber;

	for(e = emitter.begin() ; e != emitter.end() ; e++ )
	{
		// Update particles
		p = e->particle;
		while(p != NULL)
		{
			if(updateParticle(p, *e))
			{
				(*viewport.get_window_gc())->draw_pixel(p->pos.x, p->pos.y, CL_Color(
					p->color.red * 255,
					p->color.green * 255,
					p->color.blue * 255,
					p->color.alpha * 255
				));
				p = p->next;
			}
		}

		// Add particles
		emitNumber = e->emitsPerFrame + (int)( e->emitVar * variance() );

		for(emitNumber = emitNumber;
			emitNumber > 0; emitNumber--)
		{
			addParticle( *e );
		}
	}
}

bool ParticleManager::updateParticle(Particle *&p, Emitter &e)
{
	if(p != NULL){
		if(p->life > 0)
		{
			// Apply velocity to position and acceleration to velocity
			p->pos.x += p->vel.x; p->pos.y += p->vel.y;
			p->vel.x += e.acceleration.x; p->vel.y += e.acceleration.y;

			// Update particle colour
			p->color.red += p->deltaColor.red;
			p->color.green += p->deltaColor.green;
			p->color.blue += p->deltaColor.blue;
			p->color.alpha += p->deltaColor.alpha;

			p->life--;

			return true;
		}
		else
		{
			// Move the particle to the pool
			if(p->prev != NULL)
				p->prev->next = p->next;
			else
				e.particle = p->next;
			if(p->next != NULL)
				p->next->prev = p->prev;
			
			temp = p->next;
			particlePool->prev = p;
			p->next = particlePool;
			particlePool = p;
			p = temp;
			e.particleCount--;
			return false;
		}
	}
	return false;
}

void ParticleManager::addParticle(Emitter &e)
{
	Particle *p;
	CL_Colorf start, end;
	float angle, speed;


	if(&e != NULL && particlePool != NULL && e.particleCount < e.totalParticles)
	{
		p = particlePool;
		particlePool = particlePool->next;
		if(particlePool != NULL)
			particlePool->prev = NULL;

		if(e.particle != NULL)
			e.particle->prev = p;
		p->next = e.particle;
		p->prev = NULL;
		e.particle = p;
		e.particleCount++;

		p->pos = e.pos;

		angle = e.angle + ( e.angleVar * variance() );
		speed = e.speed + ( e.speedVar * variance() );
		p->vel = CL_Vector2(
			cos(angle) * speed,
			sin(angle) * speed
		);

		start.red = e.startColor.red + (e.startColorVar.red * variance() );
		start.green = e.startColor.green + (e.startColorVar.green * variance() );
		start.blue = e.startColor.blue + (e.startColorVar.blue * variance() );
		start.alpha = e.startColor.alpha + (e.startColorVar.alpha * variance() );
		end.red = e.endColor.red + (e.endColorVar.red * variance() );
		end.green = e.endColor.green + (e.endColorVar.green * variance() );
		end.blue = e.endColor.blue + (e.endColorVar.blue * variance() );
		end.alpha = e.endColor.alpha + (e.endColorVar.alpha * variance() );

		p->color.red = start.red;
		p->color.green = start.green;
		p->color.blue = start.blue;
		p->color.alpha = start.alpha;

		p->life = e.life + (int)( e.lifeVar * variance() );

		p->deltaColor.red = (end.red - start.red ) / p->life;
		p->deltaColor.green = (end.green - start.green ) / p->life;
		p->deltaColor.blue = (end.blue - start.blue ) / p->life;
		p->deltaColor.alpha = (end.alpha - start.alpha ) / p->life;
	}
}

inline float ParticleManager::variance()
{
	return random.gaussRand();
}